/* 
 * unoServer.java
 * Proof of Concept
 */

package uno.net;

import java.rmi.*;
import java.rmi.server.*;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.util.*;

import uno.utilities.Configurator;

public class UnoServer 
	extends UnicastRemoteObject
	implements UnoServerRM
{
	
	/*
	 * implementa l'interfaccia unoServerRM
	 * in particolare i metodi remoti dichiarati
	 */
	
	
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = -7905482378141407609L;
	
	/**
	 * Oggetto che gestisce il file di configurazione
	 */
	private static Configurator configManager = null;
	private void setConfigurator(Configurator givenCFG) { configManager = givenCFG; }
	private Configurator getConfigurator() { return configManager; }
	
	private String rmiName;
	public String getRmiName() { return rmiName; }
	
	/*
	 * get:set serverPort
	 * serverPort: il numero della porta dell'RMI registry, utile all'invocazione
	 * dei metodi remoti di cui si e fatto il bind
	 */
	private int serverPort;
	public void setServerPort(int port){serverPort = port;}
	public int getServerPort(){return serverPort;}
	
	
	/**
	 * get minNumOfPlayers: numero minimo di giocatori iscritti
	 */
	private int minNumOfPlayers;
	public void setMinNumOfPlayers(int num){minNumOfPlayers = num;}
	public int getMinNumOfPlayers(){return minNumOfPlayers;}
	
		
	/**
	 * Lista di id dei giocatori
	 * */
	private LinkedList<UnoPlayerID> playersList;
	public LinkedList<UnoPlayerID> getPlayersList() { return playersList; }
	
	/**
	 * Thread Invoker: invoca i thread id UNO
	 * get:set Thread Invoker
	 */
	private UnoThreadFactory threadFactory;
	private void setThreadFactory(UnoThreadFactory givenTF) { threadFactory = givenTF; }
	private UnoThreadFactory getThreadFactory() { return threadFactory;}
	
	// costruttore
	public UnoServer() throws RemoteException {
		this.setConfigurator(new Configurator(Configurator.TYPE_SERVER));
		
		String minNumOfPlayersFromFile = this.getConfigurator().getMinPlayersNumber();
		if(!minNumOfPlayersFromFile.equals(""))
			this.setMinNumOfPlayers(Integer.parseInt(minNumOfPlayersFromFile));

		this.setServerPort(Integer.parseInt(this.getConfigurator().getRmiPort()));
		this.playersList = new LinkedList<UnoPlayerID>();
		this.setThreadFactory(new UnoThreadFactory(this));
	}

	////////////////////////////////////////////////
	// inzio delle implementazione dei metodi remoti
	////////////////////////////////////////////////
	
	public boolean SignIn(String name, int port) throws RemoteException {
		boolean signed = true;
		
		try {
			// aggiorno la tabella inserendo l'utente che ha fatto richiesta
			// ip:portaRMI, nome
			playersList.add(new UnoPlayerID(RemoteServer.getClientHost() + ":" + port, name));
			System.out.println("* iscrizione effettuata per l'utente " + name);
			System.out.println("* tabella degli utenti iscritti:");
			System.out.println("* " + playersList.toString());
						
		} catch (Exception e) {
			signed = false;
			System.out.println("* ERRORE: eccezione durante l'iscrizione al server per l'utente " + name + "\n");
			e.printStackTrace();
			
		}
		return signed;
	}
	
	///////////////////////////////////////////////
	// fine delle implementazioni dei metodi remoti
	///////////////////////////////////////////////
	
	public void syncPlayers() {
		
			System.out.println("* inizio procedura di sincronizzazione tra i giocatori...");
			
			// per ogni giocatore iscritto invio la tabella contenente la lista dei giocatori
			for(Iterator<UnoPlayerID> i = playersList.iterator(); i.hasNext(); ){
				String address = i.next().getAddress();
				
				// costruisco l'URL per l'invocazione dei metodi remoti del player
				String remotePlayer = "rmi://" + address + "/unoPlayerRM";
				System.out.println("* preparazione invio della tabella dei giocatori a: URL remoto: " + remotePlayer);
				try {
					UnoPlayerRM UP = (UnoPlayerRM)Naming.lookup(remotePlayer);
					
					// invio la lista degli utenti
					if (UP.getPlayersList(playersList)) {
						System.out.println("* tabella dei giocatori inviata correttamente a: URL remoto: " + remotePlayer);
					} else {
						System.out.println("* invio della tabella dei giocatori fallito a: URL remoto: " + remotePlayer);
					}
					
				} catch (MalformedURLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NotBoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
	}

		
	// carica l'RMI registry sulla porta con valore serverPort
	public void launchRMIRegistry() {
		try {
			 java.rmi.registry.LocateRegistry.createRegistry(this.getServerPort());
			 System.out.println("* RMIRegistry pronto.");
		  } catch (Exception e) {
			 System.out.println("* ERRORE: eccezione durante il caricamento del RMIRegistry.\n");
			 e.printStackTrace();
		  }			
	}
	
	
	// effettua il bind dell'oggetto per l'invocazione remota dei metodi
	public void BindService() throws RemoteException, MalformedURLException {
		// compongo l'url di ogni singolo servizio
		
		//TODO: propabilmente getHostName andrà sostituito con InetAddress.getLocalHost().getHostAddress()
		this.rmiName = "rmi://" + UnoServer.getMyHostAddress() + ":" + this.getServerPort() + "/unoServerRM";
		Naming.rebind(this.rmiName, this);
		System.out.println("* url: " + this.rmiName);
		System.out.println("* bind dell'oggetto remoto effettuato.");
	}
	
	public static String getMyHostAddress() {
		String address = null;
		try {		
			InetAddress inet = InetAddress.getLocalHost();
			address = inet.getHostAddress();
		} catch(Exception e) {
			e.printStackTrace();
		} 
		return address;
	}
	
	public static void start() {
		try {
			System.out.println("* avvio il server...");
			// istanzia l'oggetto della unoServer
			// i parametri di configurazione vengono presi dal file di configurazione relativo
			UnoServer US = new UnoServer();
		
			// lancia RMIRegistry
			US.launchRMIRegistry();
			
			// effettua il bind dei servizi
			US.BindService();
			
			// inizializzo e avvio il thread di attesa delle iscrizioni
			US.getThreadFactory().createWaitThread().start();
			// in attesa
			System.out.println("* server in attesa...");
		} catch (Exception e) {
			e.getMessage();
			e.printStackTrace();
		}	
	}
}